<html>

<head>
<title>Final Project</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">

<script type="text/javascript" src="Scripts/glMatrix-0.9.5.min.js"></script>
<script type="text/javascript" src="Scripts/webgl-utils.js"></script>
<script type="text/javascript" src="Scripts/loaders.js"></script>
<script type="text/javascript" src="Scripts/init.js"></script>
<script type="text/javascript" src="Scripts/templates.js"></script>
<script type="text/javascript" src="Scripts/keyHandler.js"></script>

<script id="fur-fs" type="x-shader/x-fragment"></script>
<script id="fur-vs" type="x-shader/x-vertex"></script>
<script id="shader-cloud-fs" type="x-shader/x-fragment"></script>
<script id="shader-cloud-vs" type="x-shader/x-vertex"></script>
<script id="shader-water-fs" type="x-shader/x-fragment"></script>
<script id="shader-water-vs" type="x-shader/x-vertex"></script>
<script id="shader-sun-fs" type="x-shader/x-fragment"></script>
<script id="shader-sun-vs" type="x-shader/x-vertex"></script>
<script id="uv-fs" type="x-shader/x-fragment"></script>
<script id="uv-vs" type="x-shader/x-vertex"></script>
<script id="cube-fs" type="x-shader/x-fragment"></script>
<script id="cube-vs" type="x-shader/x-vertex"></script>



<script type="text/javascript">

  var perm;
  var grad3;
  var grad4;
 
        
	/*templates are used in order to make intellisense work...helps with debugging and typos*/
	var gl , canvas;
	var pMatrix = mat4.create(), mvMatrix = mat4.create();
	var mvMatrixStack = [];

			
	var furObj = [fur_template("tiger.json", ["furNoise.png", "tiger.jpg"],25,0.4,0.001)];
	var all_textures = [];
	var planeObj = new Object();
	planeObj.loaded = false;
	
	var furShader = shader_template(2);
	/*additional uniforms unique to fur shader*/
	furShader.passIndexUniform = {};
	furShader.shellDistanceUniform = {};
	furShader.dampenUniform = {};
			
	var uvShader = shader_template(1);
	var cubeShader = shader_template(1);
	
	
	
	var pitch = 0;
    var pitchRate = 0;

    var yaw = 0;
    var yawRate = 0;
        
    var xPos = 0;
    var yPos = 0.4;
    var zPos = 0;
	
	var speed = 0;
	var offsetScalar = 0.01;
	
	

	
	function mvPushMatrix() {
		var copy = mat4.create();
		mat4.set(mvMatrix, copy);
		mvMatrixStack.push(copy);
	}

	function mvPopMatrix() {
		if (mvMatrixStack.length == 0) {
			throw "Invalid popMatrix!";
		}
		mvMatrix = mvMatrixStack.pop();
	}

	function setMatrixUniforms(shaderProgram) {
		gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
		gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
	}
  
	function degToRad(degrees) {
		return degrees * Math.PI / 180;
	}
	
	var sphereVertexPositionBuffer;
    var sphereVertexTextureCoordBuffer;
    var sphereVertexIndexBuffer;

	var cubeVertexPositionBuffer;
	var cubeVertexTextureCoordBuffer;
	var cubeVertexIndexBuffer;
	
	var waterVertexPositionBuffer;
	var waterVertexTextureCoordBuffer;
	var waterVertexNormalBuffer;
	var waterVertexIndexBuffer;
	
	function drawScene() {
		gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
		gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
		gl.clearColor(0.2,0.2,0.75,1.0);
		

		mat4.perspective(67, gl.viewportWidth / gl.viewportHeight, 0.1, 100000.0, pMatrix);
		
		mat4.rotate(pMatrix, degToRad(-pitch), [1, 0, 0]);
        mat4.rotate(pMatrix, degToRad(-yaw), [0, 1, 0]);
        mat4.translate(pMatrix, [-xPos, -yPos, -zPos]);
	 
	 	mvPushMatrix();
        mat4.identity(mvMatrix);
		mat4.translate(mvMatrix, [0, -1.0, -5.0]);		
		//mat4.translate(mvMatrix, [0, 2, -5]);
		//mat4.scale(mvMatrix, [0.2, 0.2, 0.2]);
		
		
		gl.disable(gl.BLEND);
			
		drawWater();
		
		
		gl.enable(gl.BLEND);
		
		gl.blendFunc(gl.ONE_MINUS_SRC_COLOR  , gl.SRC_ALPHA);
		drawSun();
		gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);
		
		drawClouds();
		drawCubemap();
		
		

		gl.blendFunc(gl.SRC_ALPHA,gl.ONE);
		//drawSun();
		drawTiger();
		mvPopMatrix();
	}
	
	function drawCubemap() {
	
		var drawCubeMap = document.getElementById("cubeMap").checked;
		
		if (drawCubeMap) {
	
			mvPushMatrix();
			//gl.disable(gl.BLEND);
			
			gl.useProgram(cubeShader.program);
			mat4.translate(mvMatrix, [0,2,0]);
			mat4.scale(mvMatrix,[100,100,100]);
			gl.bindBuffer(gl.ARRAY_BUFFER, sphereVertexPositionBuffer);
			gl.vertexAttribPointer(cubeShader.vertexPositionAttribute, sphereVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

			gl.activeTexture(gl.TEXTURE0);
			gl.bindTexture(gl.TEXTURE_CUBE_MAP, cubemap_texture);
			gl.uniform1i(cubeShader.textureUniforms[0], 0);

			gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sphereVertexIndexBuffer);
			gl.uniformMatrix4fv(cubeShader.pMatrixUniform, false, pMatrix);
			gl.uniformMatrix4fv(cubeShader.mvMatrixUniform, false, mvMatrix);
			gl.drawElements(gl.TRIANGLES, sphereVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
			mvPopMatrix();
		}
	}
	
	
	function drawClouds() {
	
		var drawClouds = document.getElementById("clouds").checked;
		
		if (drawClouds) {
	
			mvPushMatrix();
			mat4.translate(mvMatrix,[0,2,0]);
			mat4.scale(mvMatrix,[450,450,450]);
			//mat4.rotate(mvMatrix, degToRad(-90), [1.0, 0.0, 0.0]);
			
			
			//gl.enable(gl.BLEND);
			gl.useProgram(cloudShaderProgram);
			
			gl.uniform1f(cloudShaderProgram.timeUniform, in_out);
		
			gl.bindBuffer(gl.ARRAY_BUFFER, sphereVertexPositionBuffer);
			gl.vertexAttribPointer(cloudShaderProgram.vertexPositionAttribute, sphereVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

			gl.bindBuffer(gl.ARRAY_BUFFER, sphereVertexTextureCoordBuffer);
			gl.vertexAttribPointer(cloudShaderProgram.textureCoordAttribute, sphereVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

			gl.activeTexture(gl.TEXTURE0);
			gl.bindTexture(gl.TEXTURE_2D, permTexture);
			gl.uniform1i(cloudShaderProgram.permSampler, 0);

			gl.activeTexture(gl.TEXTURE1);
			gl.bindTexture(gl.TEXTURE_2D, gradTexture);
			gl.uniform1i(cloudShaderProgram.gradSampler, 1);

			gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sphereVertexIndexBuffer);
			gl.uniformMatrix4fv(cloudShaderProgram.pMatrixUniform, false, pMatrix);
			gl.uniformMatrix4fv(cloudShaderProgram.mvMatrixUniform, false, mvMatrix);
			gl.drawElements(gl.TRIANGLES, sphereVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
			mvPopMatrix();
		}
	}
	
	function drawWater() {
		
		if (!planeObj.loaded) return;
		
		var drawWater = document.getElementById("water").checked;
		
		if (drawWater) {
		
			mvPushMatrix();	
			
			gl.useProgram(waterShaderProgram);
			gl.uniform3f(waterShaderProgram.ambientColorUniform, 0.2, 0.2, 0.1);
			gl.uniform3f(waterShaderProgram.pointLightingLocationUniform, 0, 3, -8);
			gl.uniform3f(waterShaderProgram.pointLightingSpecularColorUniform, 0.5, 0.5, 0.5);
			gl.uniform3f(waterShaderProgram.pointLightingDiffuseColorUniform, 0.3, 0.8, 0.8);
			
			mat4.scale(mvMatrix, [1000,1000,1000]);
			//mat4.rotate(mvMatrix, degToRad(-90), [1.0, 0.0, 0.0]);
			
			
			gl.uniform1f(waterShaderProgram.timeUniform, in_out);

			gl.bindBuffer(gl.ARRAY_BUFFER, planeObj.positionBuffer);
			gl.vertexAttribPointer(waterShaderProgram.vertexPositionAttribute, planeObj.positionBuffer.itemSize, gl.FLOAT, false, 0, 0);
			
			gl.bindBuffer(gl.ARRAY_BUFFER, planeObj.normalBuffer);
			gl.vertexAttribPointer(waterShaderProgram.vertexNormalAttribute, planeObj.normalBuffer.itemSize, gl.FLOAT, false, 0, 0);
		
			gl.bindBuffer(gl.ARRAY_BUFFER, planeObj.texBuffer);
			gl.vertexAttribPointer(waterShaderProgram.textureCoordAttribute, planeObj.texBuffer.itemSize, gl.FLOAT, false, 0, 0);

			gl.activeTexture(gl.TEXTURE0);
			gl.bindTexture(gl.TEXTURE_2D, permTexture);
			gl.uniform1i(waterShaderProgram.permSampler, 0);

			gl.activeTexture(gl.TEXTURE1);
			gl.bindTexture(gl.TEXTURE_2D, gradTexture);
			gl.uniform1i(waterShaderProgram.gradSampler, 1);
			
			gl.activeTexture(gl.TEXTURE2);
			gl.bindTexture(gl.TEXTURE_CUBE_MAP, cubemap_texture);
			gl.uniform1i(waterShaderProgram.cubemapTextureUniform, 2);
			
			gl.activeTexture(gl.TEXTURE3);
			gl.bindTexture(gl.TEXTURE_CUBE_MAP, noise_texture);
			gl.uniform1i(waterShaderProgram.noiseTextureUniform, 3);

			gl.uniform3f(waterShaderProgram.camPositionUniform, pMatrix[3], pMatrix[7], pMatrix[11]);
			

			gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, planeObj.indexBuffer);
			
			gl.uniformMatrix4fv(waterShaderProgram.pMatrixUniform, false, pMatrix);
			gl.uniformMatrix4fv(waterShaderProgram.mvMatrixUniform, false, mvMatrix);
			
			var normalMatrix = mat3.create();
			mat4.toInverseMat3(mvMatrix, normalMatrix);
			mat3.transpose(normalMatrix);
			gl.uniformMatrix3fv(waterShaderProgram.nMatrixUniform, false, normalMatrix);
			
			gl.drawElements(gl.TRIANGLES, planeObj.indexBuffer.numItems, gl.UNSIGNED_SHORT, 0);     
			mvPopMatrix();
		}
	}
	
	
	
	function drawTiger() {
		
		var drawTiger = document.getElementById("tiger").checked;
		
		if (drawTiger) {
	
			mvPushMatrix();
			//render furry objects
			
			for (var i=0;i<furObj.length;i++) {
				
				if (furObj[i].loaded == false) continue;
				
				var posBuffer = furObj[i].positionBuffer;
				var texBuffer = furObj[i].texBuffer;
				var normBuffer = furObj[i].normalBuffer;
				var indBuffer = furObj[i].indexBuffer;
			
				mvMatrix = furObj[i].mvMatrix;
				mat4.identity(mvMatrix);
				mat4.translate(mvMatrix, [0, 0.3, -5]);
				//mat4.scale(mvMatrix, [1.3, 1.3, 1.3]);
				mat4.rotate(mvMatrix, degToRad(objectAngle),[0,1,0]);
			
				gl.activeTexture(gl.TEXTURE0);
				gl.bindTexture(gl.TEXTURE_2D, furObj[i].textures[0]);
				gl.activeTexture(gl.TEXTURE1);
				gl.bindTexture(gl.TEXTURE_2D, furObj[i].textures[1]);
			
				gl.disable(gl.BLEND);

				gl.useProgram(uvShader.program);
				gl.uniformMatrix4fv(uvShader.pMatrixUniform, false, pMatrix);
				gl.uniform1i(uvShader.textureUniforms[0],1);
				gl.uniformMatrix4fv(uvShader.mvMatrixUniform, false, mvMatrix);
			
				gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
				gl.vertexAttribPointer(uvShader.vertexPositionAttribute, posBuffer.itemSize, gl.FLOAT, false ,0, 0);
				gl.bindBuffer(gl.ARRAY_BUFFER, texBuffer);
				gl.vertexAttribPointer(uvShader.vertexTexCoordAttribute, texBuffer.itemSize, gl.FLOAT, false, 0, 0);
				gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indBuffer);
			
				gl.drawElements(gl.TRIANGLES, indBuffer.numItems, gl.UNSIGNED_SHORT,0);
			

				gl.enable(gl.BLEND);
				gl.useProgram(furShader.program);
			
				gl.uniform1i(furShader.textureUniforms[0], 0);
				gl.uniform1i(furShader.textureUniforms[1], 1);
				gl.uniformMatrix4fv(furShader.pMatrixUniform, false, pMatrix);
				gl.uniformMatrix4fv(furShader.mvMatrixUniform, false, mvMatrix);
				gl.uniform1f(furShader.dampenUniform, furObj[i].dampen);
				gl.uniform1f(furShader.shellDistanceUniform, furObj[i].shellDistance);
			
				gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
				gl.vertexAttribPointer(furShader.vertexPositionAttribute, posBuffer.itemSize, gl.FLOAT, false, 0, 0);
				gl.bindBuffer(gl.ARRAY_BUFFER, texBuffer);
				gl.vertexAttribPointer(furShader.vertexTexCoordAttribute, texBuffer.itemSize, gl.FLOAT, false, 0, 0);
				gl.bindBuffer(gl.ARRAY_BUFFER, normBuffer);
				gl.vertexAttribPointer(furShader.vertexNormalAttribute, normBuffer.itemSize, gl.FLOAT, false, 0, 0);

				for (var j=1;j<furObj[i].passes;j++) {
					gl.uniform1f(furShader.passIndexUniform, j);
					gl.drawElements(gl.TRIANGLES, indBuffer.numItems, gl.UNSIGNED_SHORT,0);
				}
			}
			mvPopMatrix();
		}
	}
	
	function drawSun() {

		var drawSun = document.getElementById("sun").checked;
		
		if (drawSun) {
	
			mvPushMatrix();
			
			gl.useProgram(sunShaderProgram);
			mat4.rotate(mvMatrix, 89.5, [0, 1, 0]);
			
			mat4.translate(mvMatrix, [0.0, 150.0, -800.0]);
			mat4.scale(mvMatrix, [120, 120, 120]);
			
			gl.uniform1f(sunShaderProgram.timeUniform, in_out);

			gl.bindBuffer(gl.ARRAY_BUFFER, sphereVertexPositionBuffer);
			gl.vertexAttribPointer(sunShaderProgram.vertexPositionAttribute, sphereVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

			gl.bindBuffer(gl.ARRAY_BUFFER, sphereVertexTextureCoordBuffer);
			gl.vertexAttribPointer(sunShaderProgram.textureCoordAttribute, sphereVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
		   
			gl.activeTexture(gl.TEXTURE0);
			gl.bindTexture(gl.TEXTURE_2D, permTexture);
			gl.uniform1i(sunShaderProgram.permSampler, 0);

			gl.activeTexture(gl.TEXTURE1);
			gl.bindTexture(gl.TEXTURE_2D, gradTexture);
			gl.uniform1i(sunShaderProgram.gradSampler, 1);
			
		

			gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sphereVertexIndexBuffer);
			
			gl.uniformMatrix4fv(sunShaderProgram.pMatrixUniform, false, pMatrix);
			gl.uniformMatrix4fv(sunShaderProgram.mvMatrixUniform, false, mvMatrix);
			gl.drawElements(gl.TRIANGLES, sphereVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
			
			mvPopMatrix();
		}
	}
	

	var lastTime = 0;
	var objectAngle = 180;
	var joggingAngle = 0;
	var xRot = 0;
	var yRot = 0;
	var zRot = 0;
	var in_out = 0;
	function animate() {
		var dateTime = new Date();
		var timeNow = dateTime.getTime();
		if (lastTime != 0) {
			var elapsed = timeNow - lastTime;
			
			if (speed != 0) {
                xPos -= Math.sin(degToRad(yaw)) * speed * elapsed;
                zPos -= Math.cos(degToRad(yaw)) * speed * elapsed;

                joggingAngle += elapsed * 0.6; // 0.6 "fiddle factor" - makes it feel more realistic :-)
                yPos = Math.sin(degToRad(joggingAngle)) / 20 + 0.4
			}
			
			yaw += yawRate * elapsed;
            pitch += pitchRate * elapsed;
            
            //keeps viewer from vertically over/under rotating view
            if (pitch < -50.0)
                pitch = -50;
            if (pitch > 50.0)
                pitch = 50;
			
			var seconds = timeNow / 1000.0;
			var interval = 65536.0;
			var mod_outer = seconds % (interval * 2.0);
			var mod_inner = seconds % interval;
			in_out = 0;
			if (mod_outer > interval) { in_out = interval - mod_inner; }
			else { in_out = mod_outer; }
			var elapsed_scaled = elapsed / 10.0;
			xRot += (90 * elapsed_scaled) / 1000.0;
			yRot += (90 * elapsed_scaled) / 1000.0;
			zRot += (90 * elapsed_scaled) / 1000.0;
			objectAngle += 0.05 * elapsed;
		}
		lastTime = timeNow;
	  }

	var canvas;
	function webGLStart() {
		canvas = document.getElementById("webgl-canvas");
		initGL(canvas);
		resize();
		initData();
		initShaders();
		initTextures();
		initBuffers();

		initPermTexture();
		initGradTexture();
		
		loadObjects();
		
		
		gl.clearColor(1.0, 1.0, 1.0, 0.0);
	
		//gl.clearDepth(1.0);

		gl.enable(gl.DEPTH_TEST);
		gl.depthFunc(gl.LEQUAL);
		gl.blendEquation(gl.ADD);
		//gl.blendFunc(gl.DST_COLOR, gl.SRC_ALPHA);
		//gl.blendFunc(gl.SRC_COLOR, gl.DST_ALPHA); //-- leave this here..alternate blending function..need to test them some more to find the best one
		gl.blendFuncSeparate(gl.SRC_COLOR, gl.DST_ALPHA, gl.SRC_COLOR, gl.DST_ALPHA); //was src_alpha, one, one_minus_dst_alpha, dst_alpha
		
		document.onkeydown = handleKeyDown;
        document.onkeyup = handleKeyUp;

		tick();
	}

	function tick() {
		requestAnimFrame(tick);
		handleKeys();
		drawScene();
		animate();
	}
  
	function resize() {
		gl.viewportWidth = canvas.width = window.innerWidth;
		gl.viewportHeight = canvas.height = window.innerHeight;
	}

</script>

<style type="text/css">
	#loadingtext {
		position: absolute;
		top: 250px;
		left: 150px;
		font-size: 2em;
		color: white;
	}
</style>

</head>
<body onload="webGLStart();" onresize="resize()">
	<canvas id="webgl-canvas" style="border: none;" width="1024" height="768"></canvas>
	<div id="loadingtext">
		Loading world...
	</div>
	<br />
	<input type="checkbox" id="water" checked />Render Water
	<input type="checkbox" id="clouds" checked />Render Clouds
	<input type="checkbox" id="tiger" checked />Render Tiger
	<input type="checkbox" id="sun" checked />Render Sun
	<input type="checkbox" id="cubeMap" checked />Render Cube Map
	
</body>
</html>